package com.bobohost.crmrbac.config;

import com.bobohost.crmrbac.pojo.po.*;
import com.bobohost.crmrbac.service.*;
import com.bobohost.crmrbac.pojo.po.*;
import com.bobohost.crmrbac.service.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.lang.util.ByteSource;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;

/**
 * 自定义的realm
 */
@Slf4j
//@Component
//@RequiredArgsConstructor
public class MyRealm extends AuthorizingRealm {

    //注入员工的service
    @Autowired
    private EmployeeService employeeService;

    //注入角色的service
    @Autowired
    private RoleService roleService;

    //注入权限数据service
    @Autowired
    private PermissionService permissionService;


    //注入员工角色关联数据Service
    @Autowired
    private EmployeeRoleService employeeRoleService;

    //注入角色权限关联数据service
    @Autowired
    private RolePermissionService rolePermissionService;



    /**
     * 授权数据提供
     * @param principals the primary identifying principals of the AuthorizationInfo that should be retrieved.
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        log.debug("MyRealm的doGetAuthorizationInfo方法被调用了---授权数据");


        //-------------写死的授权数据
        //构建一个简单的授权信息对象
        //写法1：
//        AuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
//        authorizationInfo.getRoles().add("HR_MGR");

        //写法2：
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        /*//先写死权限--不管哪个用户都有某角色或权限
//        //添加一个角色HR_MGR
//        authorizationInfo.addRole("HR_MGR");
//        //添加一个权限：customer:potentialList
//        authorizationInfo.addStringPermission("customer:potentialList");

        //加点料：判断当前用户是否是admin，是，则给权限，不是，则不给
        //获取当前用户对象方法1：
        Employee employee = (Employee) principals.getPrimaryPrincipal();

        //获取当前用户对象方法2：
//        Employee employee = (Employee)SecurityUtils.getSubject().getPrincipal();

        if("admin".equals(employee.getUsername())){
            //先写死权限--不管哪个用户都有某角色或权限
            //添加一个角色HR_MGR
            authorizationInfo.addRole("HR_MGR");
            //添加一个权限：customer:potentialList
            authorizationInfo.addStringPermission("customer:potentialList");
        }

        */

        //----------------------------------
        //将数据写活
        //目标：根据当前用户获取角色和权限
        //1.获取当前用户
        //方式1：推荐
        Employee user = (Employee) principals.getPrimaryPrincipal();
        //方式2：
//        Employee user  = (Employee)SecurityUtils.getSubject().getPrincipal();

        //--判断用户是否是超级管理员
        if("admin".equals(user.getUsername())){
            //管理员用户：拥有所有角色数据和权限数据
            //查询所有角色
            List<Role> roleList= roleService.list();
            //将角色列表放入授权信息中
            for(Role role : roleList){
                authorizationInfo.addRole(role.getSn());
            }

            //查询所有权限
            List<Permission> permissionList = permissionService.list();
            //将权限列表放入授权信息中
            for(Permission permission : permissionList){
                authorizationInfo.addStringPermission(permission.getExpression());
            }

        }else{
            //普通用户:根据RBAC表查询角色和权限

            //=====角色数据
            //查询当前用户拥有的员工角色关联表的数据
            List<EmployeeRole> employeeRoleList=employeeRoleService.findEmployeeRoleListByEmployeeId(user.getId());
            //循环员工角色关联表的数据进行查询角色的数据
            for(EmployeeRole employeeRole : employeeRoleList){
                //获取角色id
                Long roleId = employeeRole.getRoleId();

                //根据角色id查询角色数据
                Role role = roleService.getById(roleId);
                //将角色放入授权信息中
                authorizationInfo.addRole(role.getSn());


                //=====权限数据
                //根据角色id查询角色权限关联的数据
                List<RolePermission> rolePermissionList = rolePermissionService.findRolePermissionListByRoleId(roleId);
                //循环角色权限关联表的数据进行查询权限的数据
                for (RolePermission rolePermission:rolePermissionList){
                    //获取权限id
                    Long permissionId = rolePermission.getPermissionId();
                    //根据权限id查询权限数据
                    Permission permission = permissionService.getById(permissionId);
                    //将权限放入授权信息中
                    authorizationInfo.addStringPermission(permission.getExpression());
                }

            }

        }

        return authorizationInfo;
    }




    /**
     * 密码加密要用到的Matcher
     */
    private CredentialsMatcher credentialsMatcher;

    @Override
    public CredentialsMatcher getCredentialsMatcher() {
        return credentialsMatcher;
    }

    @Override
    public void setCredentialsMatcher( CredentialsMatcher credentialsMatcher) {
        this.credentialsMatcher = credentialsMatcher;
    }

    /**
     * 认证数据提供，安全管理器来调用该方法
     * @param authenticationToken the authentication token containing the user's principal and credentials.
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
       log.debug("MyRealm的doGetAuthenticationInfo方法被调用了---认证数据");


        //获取用户密码令牌
        UsernamePasswordToken usernamePasswordToken =(UsernamePasswordToken)authenticationToken;

        //获取用户名
        String username = usernamePasswordToken.getUsername();


        //通过用户名到数据库查询用户
        Employee user=employeeService.findEmployeeByUsername(username);

        //判断用户(名)是否存在
        if(null == user){
            //如果用户不存在，则返回空，安全管理器会自动抛出异常：UnknownAcccount的异常
            return null;
        }

        //对登录时用户输入的明文密码进行加密
        //参数1：算法,参数2：明文密码，参数3：用户名做盐，参数4：加密次数
//        SimpleHash simpleHash = new SimpleHash("sha256",usernamePasswordToken.getPassword(),
//                ByteSource.Util.bytes(username), 1024);
//        //将用户对象的密码换掉
//        user.setPassword(hashPassword.toString());

        //加密后的密码：
//        String hashPassword = simpleHash.toHex();

        //将查询到的用户信息和用户登录信息都封装到AuthenticationInfo接口对象中，交给安全管理器就行了
        //参数1：首要用户：用户对象，这里必须用用户对象，在授权的地方才能取出来
        //参数2：加密卡：用户密码，数据库加密的密码
        //参数3：当前realm的名称(设计上，realm可以有多个)
//        AuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user,user.getPassword(),"");
//        AuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user,user.getPassword(),getName());

        //改进：密码加密校验
//        AuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user,user.getPassword(),"");

        //这个类会比对页面传过来的密码和数据库的密码
//        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(username, user.getPassword(), getName());
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user, user.getPassword(), getName());
        //单独设置盐,交给安全管理器
        authenticationInfo.setCredentialsSalt(ByteSource.Util.bytes(username));


        return authenticationInfo;
    }


}
